//
//  parse_nt.cpp
//  cobalt
//
//  Created by Nicolas Godbout on 2013-08-21.
//  Copyright (c) 2013 Nicolas Godbout. All rights reserved.
//

#include "parse_nt.h"

bool
ParseNT::parse(const std::string& input) {
    setInput(input);
    return ntripleDoc();
}

bool
ParseNT::ntripleDoc() {
    status = OK_NC;
    std::list<Triple> triples = many(&ParseNT::line);
    eof();
    triples.remove_if([](Triple t){ return !t; });
    graph.add(triples);
    if ( status == OK || status == OK_NC )
        return true;
    else
        return false;
}


inline Triple
ParseNT::line() {
    NOERR
    Triple ret;
    manyWS();
    ALTS_BEGIN
    comment();
    eoln();
    ALT
    ret = triple();
    eoln();
    ALT
    eoln();
    ALTS_END
}

inline void
ParseNT::comment() {
    match(re_Comment);
}

inline Triple
ParseNT::triple() {
    NOERR
    RDFNode s(subject());
    someWS();
    RDFNode p(predicate());
    someWS();
    RDFNode o(object());
    manyWS();
    character('.');
    manyWS();
    RETURN( env->createTriple(s, p, o) )
    return {};
}

inline RDFNode
ParseNT::subject() {
    NOERR
    ALTS_BEGIN
    RDFNode ret(uriref());
    ALT
    ret = nodeID();
    ALTS_END
}

inline RDFNode
ParseNT::predicate() {
    return uriref();
}

inline RDFNode
ParseNT::object() {
    NOERR
    ALTS_BEGIN
    RDFNode ret(uriref());
    ALT
    ret = nodeID();
    ALT
    ret = literal();
    ALTS_END
}

inline RDFNode
ParseNT::uriref() {
    mrange_t uri = match_range(re_URIRef);
    uri.first++;
    uri.second--;
    RETURN( env->createNamedNode(std::string(uri.first, uri.second)) )
    return RDFNode();
}

inline RDFNode
ParseNT::nodeID() {
    std::string name = match(re_Blank);
    RETURN( env->createBlankNode() )
    return RDFNode();
}

inline RDFNode
ParseNT::literal() {
    NOERR
    RDFNode ret;
    mrange_t range = match_range(re_String);
    NOERR
    range.first++;
    range.second--;
    ALTS_BEGIN
    character('@');
    std::string lang(match(re_Language));
    RETURN( env->createLiteral(std::string(range.first, range.second), lang) )
    ALT
    character('^'); character('^');
    RDFNode dt(uriref());
    RETURN( env->createLiteral(std::string(range.first, range.second), dt) );
    ALT
    RETURN( env->createLiteral(std::string(range.first, range.second)) );
    ALTS_END
}

inline void
ParseNT::manyWS() {
    skipMany(&Parser::oneOf, " \t");
}

inline void
ParseNT::someWS() {
    skipSome(&Parser::oneOf, " \t");
}

bool ParseNT::inited = false;
std::regex ParseNT::re_Comment;
std::regex ParseNT::re_URIRef;
std::regex ParseNT::re_Blank;
std::regex ParseNT::re_String;
std::regex ParseNT::re_Language;

void
ParseNT::init() {
    auto flags = std::regex::optimize | std::regex::nosubs | std::regex::ECMAScript;

    re_Comment = std::regex("#[^\r\n]*", flags);
    
    re_URIRef = std::regex("<[ -=?-\[^-~]*>", flags);
    
    re_Blank = std::regex("_:[[:alpha:]_][[:alnum:]_-]*", flags);
    
    // "\"(?:(?:\\\\(?:[tnr\"\\]|u[[:xdigit:]]{4}))|(?:[] !#-\[^-~])+)*\""
    re_String = std::regex("\"(?:(?:\\\\(?:[tnr\\\\\\\"]|(?:u[[:xdigit:]]{4})))|[\\x20-\\x21\\x23-\\x5B\\x5D-\\x7E])*\"",
                           flags);

    // [a-z]+ ('-' [a-z0-9]+ )*
    re_Language = std::regex("[a-z]+(?:-[a-z0-9]+)*", flags);
    
    inited = true;
}
